﻿using System.Collections.Generic;
using System.IO;
using System.Web.Configuration;
using System.Web.Hosting;
using System.Xml;
using visinia.SmartEngine.Providers;

namespace visinia.SmartEngine.Installer
{
    internal class ModuleInstaller
    {
        #region Module Installer
        internal static bool HandleModule(string viFile, List<ContentFileInfo> ContentFileList, XmlDocument doc, XmlNode typeNode)
        {
            XmlNode dataNode = typeNode["data"];
            if (dataNode != null)
            {
                // the module must have a name;
                XmlNode nameNode = dataNode["name"];
                if (nameNode == null)
                    return false;
                string moduleName = nameNode.InnerText;
                if (string.IsNullOrEmpty(moduleName))
                    return false;

                // put the module to the General package if not specified
                string modulePackage = "General";
                XmlNode packageNode = dataNode["package"];
                if (packageNode != null)
                {
                    if (!string.IsNullOrEmpty(packageNode.InnerText))
                        modulePackage = packageNode.InnerText;
                }

                // if this module is not specified in content file don't install it either
                ContentFileInfo cfInfo = ContentFileList.Find
                    (p => ((p.Type == ContentFileType.Module) &&
                           (string.Compare(p.Name, moduleName, true) == 0) &&
                           (string.Compare(p.Package, modulePackage, true) == 0) &&
                           (p.IsInstalled == false)));

                if (cfInfo == null)
                    return false;
                // this module is already installed
                if (cfInfo.IsInstalled)
                    return false;

                // register module
                XmlNode moduleurl = dataNode["moduleurl"];
                if (moduleurl == null)
                    return false;
                if (string.IsNullOrEmpty(moduleurl.InnerText))
                    return false;
                ModuleInstalled module = new ModuleInstalled();
                module.Name = moduleName;
                module.Package = modulePackage;
                string ModuleDir = modulePackage + "/" + moduleName;
                module.ModuleUrl = Path.Combine(ModuleDir, moduleurl.InnerText);
                XmlNode modulsettingsurl = dataNode["modulesettingsurl"];
                if (modulsettingsurl == null)
                    module.ModuleSettingsUrl = null;
                else
                    module.ModuleSettingsUrl = Path.Combine(ModuleDir, modulsettingsurl.InnerText);
                module.Icon = dataNode["icon"] != null ? dataNode["icon"].InnerText : null;
                module.Image = dataNode["image"] != null ? dataNode["image"].InnerText : null;
                module.Help = dataNode["help"] != null ? dataNode["help"].InnerText : null;
                ModulesInstalledController.RegisterModule(module);

                string UninstallRoot = HostingEnvironment.MapPath(WebRoots.GetUnInstallRoot);
                string UninstallDirectoryName = UninstallRoot + modulePackage + "\\" + moduleName;
                Directory.CreateDirectory(UninstallDirectoryName);
                // save this installtion data file also for uninstallation needs                            
                string installationDataFileBkup = Path.Combine(UninstallDirectoryName, Path.GetFileName(viFile));
                doc.Save(installationDataFileBkup);

                XmlNode filesNode = typeNode["files"];
                if (filesNode != null)
                {
                    foreach (XmlNode fileNode in filesNode.ChildNodes)
                    {
                        // create files and directories in proper website folders                       
                        string directory = Path.GetDirectoryName(viFile);
                        string sourcePath = Path.Combine(directory, fileNode.InnerText);
                        if (!File.Exists(sourcePath))
                            continue;
                        switch (fileNode.Name.ToLower())
                        {
                            case "modules":// copy files to the site modules folder   
                                CopyFile(moduleName, modulePackage, fileNode, sourcePath, HostingEnvironment.MapPath(WebRoots.GetModulesRoot));
                                break;
                            //case "clientscripts":// copy files to the site client scripts folder
                            //    CopyFile(moduleName, modulePackage, fileNode, sourcePath, HostingEnvironment.MapPath(WebRoots.GetClientScriptsRoot));
                            //    break;
                            case "adminpages":// copy files to the site admin / pages folder
                                CopyFile(moduleName, modulePackage, fileNode, sourcePath, HostingEnvironment.MapPath(WebRoots.GetAdminPagesRoot));
                                break;
                            case "bin":// copy files to the site bin folder
                                if (fileNode.InnerText.EndsWith(".dll"))
                                {
                                    string binRoot = HostingEnvironment.MapPath(WebRoots.GetBinRoot);
                                    string binfilename = Path.GetFileName(fileNode.InnerText);
                                    string binsourcePath = Path.Combine(directory, binfilename);
                                    // overwrite file if already exists
                                    File.Copy(binsourcePath, binRoot + binfilename, true);
                                }
                                break;
                        }
                    }
                }
                // sql scripts
                XmlNode dataProvidersNode = typeNode["dataproviders"];
                if (dataProvidersNode != null)
                {
                    // find the current active data provider from web.config
                    DataProviderSection section = (DataProviderSection)WebConfigurationManager.GetWebApplicationSection("visinia/DataProvider");
                    string ActiveDataProvider = section.DefaultDataProvider;

                    // find the above stated data provider from the dataproviers
                    // given in the installation file
                    XmlNode activeProvidersNode = dataProvidersNode[ActiveDataProvider.ToLower()];
                    if (activeProvidersNode != null)
                    {
                        XmlNode installScript = activeProvidersNode["install"];
                        if (installScript != null)
                        {
                            // run script
                            string directory = Path.GetDirectoryName(viFile);
                            string scriptPath = Path.Combine(directory, installScript.InnerText);
                            InstallerCommon.ExecuteScript(scriptPath);

                            // put uninstall script in module's uninstall folder
                            XmlNode uninstallScript = activeProvidersNode["uninstall"];
                            if (uninstallScript != null)
                            {
                                string udirectory = Path.GetDirectoryName(viFile);
                                string sourcePath = Path.Combine(udirectory, uninstallScript.InnerText);
                                if (File.Exists(sourcePath))
                                {
                                    // put uninstall script in module's uninstall folder
                                    string uninstallScriptBkup = Path.Combine(UninstallDirectoryName, uninstallScript.InnerText);
                                    string uninstallScriptDirName = Path.GetDirectoryName(uninstallScriptBkup);
                                    if (uninstallScriptDirName.Length > 0)
                                        Directory.CreateDirectory(uninstallScriptDirName);
                                    File.Copy(sourcePath, uninstallScriptBkup, true);
                                }
                            }
                        }
                    }
                }
            }
            return true;
        }

        private static void CopyFile(string moduleName, string modulePackage, XmlNode fileNode, string sourcePath, string root)
        {
            string directoryName = root + modulePackage + "/" + moduleName;
            string destinationPath = Path.Combine(directoryName, fileNode.InnerText);
            string dirName = Path.GetDirectoryName(destinationPath);
            if (dirName.Length > 0)
                Directory.CreateDirectory(dirName);
            // overwrite file if already exists
            File.Copy(sourcePath, destinationPath, true);
        }
        #endregion        

        #region Module Uninstaller
        internal static bool UnInstallModule(int ModuleInstalledId)
        {
            ModuleInstalled installedModule = ModulesInstalledController.GetInstalledModule(ModuleInstalledId);
            if (installedModule == null)
                return false;
            
            // unregister module            
            ModulesInstalledController.UnRegisterModule(ModuleInstalledId);

            string ModuleName = installedModule.Name;
            string Package = installedModule.Package;
            string UninstallRoot = HostingEnvironment.MapPath(WebRoots.GetUnInstallRoot);
            string UninstallFolder = UninstallRoot + Package + "\\" + ModuleName;
            // uninstall folder doesn't exist
            if (!Directory.Exists(UninstallFolder))
                return false;
            // the visinia installtion data file
            string[] vi = Directory.GetFiles(UninstallFolder, "*.vi");
            // more then one installation file
            if (vi.Length > 1)
                return false;
            ParseUnInstallerFile(vi[0], UninstallFolder, Package, ModuleName);            

            // delete uninstall folder            
            string ModuleDirectoryName = UninstallRoot + Package + "\\" + ModuleName;
            if (Directory.Exists(ModuleDirectoryName))
                Directory.Delete(ModuleDirectoryName, true);
            string PackageDirectoryName = UninstallRoot + Package;
            string[] files = Directory.GetFiles(PackageDirectoryName, "*.*", SearchOption.AllDirectories);
            // delete package folder if empty
            if (files.Length == 0)
                Directory.Delete(PackageDirectoryName, true);
            return true;
        }

        private static bool ParseUnInstallerFile(string viFile, string UninstallFolder, string Package, string ModuleName)
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(viFile);
            XmlNode visiniaNode = doc.DocumentElement;
            if (visiniaNode != null)
            {
                // TODO check visinia version 

                // this is module installation data
                XmlNode typeNode = visiniaNode["module"];
                if (typeNode != null)
                {
                    XmlNode dataNode = typeNode["data"];
                    if (dataNode != null)
                    {
                        // the module must have a name;
                        XmlNode nameNode = dataNode["name"];
                        if (nameNode == null)
                            return false;
                        if (nameNode.InnerText.ToLower() != ModuleName.ToLower())
                            return false;
                        XmlNode packageNode = dataNode["package"];
                        if (packageNode != null)
                        {
                            if (packageNode.InnerText.ToLower() != Package.ToLower())
                                return false;
                        }
                        XmlNode filesNode = typeNode["files"];
                        if (filesNode != null)
                        {
                            foreach (XmlNode fileNode in filesNode.ChildNodes)
                            {
                                // delete files and directories from the proper website folders                               
                                switch (fileNode.Name.ToLower())
                                {
                                    case "modules":// remove files from the site modules folder
                                        string modulesroot = HostingEnvironment.MapPath(WebRoots.GetModulesRoot);
                                        string modulesfilePath = modulesroot + Package + "/" + ModuleName + "/" + fileNode.InnerText;
                                        if (File.Exists(modulesfilePath))
                                            SafeDelete(Package, ModuleName, fileNode, modulesroot, modulesfilePath); 
                                            break;
                                    //case "clientscripts":// remove files from the site client scripts folder
                                    //        string clientscriptsroot = HostingEnvironment.MapPath(WebRoots.GetClientScriptsRoot);
                                    //        string clientscriptsfilePath = clientscriptsroot + Package + "/" + ModuleName + "/" + fileNode.InnerText;
                                    //        if (File.Exists(clientscriptsfilePath))
                                    //            SafeDelete(Package, ModuleName, fileNode, clientscriptsroot, clientscriptsfilePath);
                                    //        break;
                                    case "adminpages":// remove files from the site admin / pages folder
                                            string adminpagesroot = HostingEnvironment.MapPath(WebRoots.GetAdminPagesRoot);
                                            string adminpagesfilePath = adminpagesroot + Package + "/" + ModuleName + "/" + fileNode.InnerText;
                                            if (File.Exists(adminpagesfilePath))
                                                SafeDelete(Package, ModuleName, fileNode, adminpagesroot, adminpagesfilePath);
                                            break;
                                    case "bin":// remove files from the site bin folder
                                            if (fileNode.InnerText.EndsWith(".dll"))
                                            {                                                
                                                string binRoot = HostingEnvironment.MapPath(WebRoots.GetBinRoot);
                                                string binfilename = Path.GetFileName(fileNode.InnerText);
                                                string binfilePath = binRoot + binfilename;
                                                if (File.Exists(binfilePath))
                                                    File.Delete(binfilePath);
                                            }
                                            break;
                                }  
                            }
                        }
                        // sql scripts
                        XmlNode dataProvidersNode = typeNode["dataproviders"];
                        if (dataProvidersNode != null)
                        {
                            // find the current active data provider from web.config
                            DataProviderSection section = (DataProviderSection)WebConfigurationManager.GetWebApplicationSection("visinia/DataProvider");
                            string ActiveDataProvider = section.DefaultDataProvider;

                            // find the above stated data provider from the dataproviers
                            // given in the installation file
                            XmlNode activeProvidersNode = dataProvidersNode[ActiveDataProvider.ToLower()];
                            if (activeProvidersNode != null)
                            {
                                XmlNode uninstallScript = activeProvidersNode["uninstall"];
                                if (uninstallScript != null)
                                {
                                    string UninstallRoot = HostingEnvironment.MapPath(WebRoots.GetUnInstallRoot);
                                    string directoryName = UninstallRoot + Package + "\\" + ModuleName;
                                    // run uninstall script
                                    string uninstallScriptBkup = Path.Combine(directoryName, uninstallScript.InnerText);
                                    if (File.Exists(uninstallScriptBkup))
                                        return InstallerCommon.ExecuteScript(uninstallScriptBkup);
                                }
                            }
                        }
                    }
                    return true;
                }
            }
            return false;
        }

        private static bool SafeDelete(string Package, string ModuleName, XmlNode fileNode, string root, string filePath)
        {
            // delete specified file
            File.Delete(filePath);

            string inner = Path.GetDirectoryName(fileNode.InnerText);
            if (!string.IsNullOrEmpty(inner))
            {
                string innerFolder = root + Package + "\\" + ModuleName + "\\" + inner;

                // don't remove folders if some data is added            
                string[] innerdirs = Directory.GetFileSystemEntries(innerFolder, "*.*");
                if (innerdirs.Length == 0)
                    Directory.Delete(innerFolder, true);
                else
                    return false;
            }
            // delete module folder if empty
            string ModuleDirectoryName = root + Package + "\\" + ModuleName;
            string[] moduledirs = Directory.GetFileSystemEntries(ModuleDirectoryName, "*.*");
            if (moduledirs.Length == 0)
                Directory.Delete(ModuleDirectoryName, true);
            else
                return false;

            // delete package folder if empty
            string PackageDirectoryName = root + Package;
            string[] packagedirs = Directory.GetFileSystemEntries(PackageDirectoryName, "*.*");
            if (packagedirs.Length == 0)
                Directory.Delete(PackageDirectoryName, true);
            else
                return false;
            return true;
        }
        #endregion
    }
}
